; program to load boot routines to tracks one & two

        title 'loadsys'
        .z80

bdos    equ 5             ; cpm entry point
cr      equ 0dh           ; carriage return
lf      equ 0ah           ; line feed
boot    equ 0             ; boot location

loadsy:                   ; entry point

; check we have cpm3
        ld c,12           ; to get version number
        call bdos
        ld a,l            ; version number in a
        cp 30h            ; version 30 or later
        jp c,oldver       ; jump if old version

; read cpmldr
        ld c,15           ; open file code
        ld de,fcb         ; fcb address
        call bdos
        and a
        jp nz,operr       ; jump if can't open file
        ld de,cpmldr
        ld c,26           ; set dma address
        call bdos
        ld e,26           ; set to read 26 sectors
        ld c,44           ; multi-record read
        call bdos
        ld de,fcb
        ld c,20           ; read instruction
        call bdos
        inc a
        jp z,rderr        ; jump if read fails
        ld e,1
        ld c,44           ; to reset multi-record count
        call bdos

; message to load target disk
ltd:    ld c,13           ; to reset disk system
        call bdos
        call clear
rp:     ld de,lddm
        ld c,9            ; output string code
        call bdos
        ld de,rbuff       ; read buff location
        ld c,10           ; read buffer
        call bdos
        ld hl,(rbuffc)    ; count to l, char to h
        ld a,l
        and a             ; get flags
        jp z,exit         ; exit if no chars entered
        ld a,h            ; character
        sub 'A'           ; change 'A' to 0
        jr c,rp           ; try again if disk not valid
        ld e,a            ; need result in e
        sub 32            ; in case lowercase
        jr c,uc           ; jump if uppercase
        ld e,a
uc:     ld a,e
        cp 3              ; check disk = A,B or C
        jr nc,rp

; select disk and get disk characteristics
        ld c,14           ; disk select code
        call bdos
        and a             ; get flags
        jp nz,slerr       ; select error
        ld c,31           ; code to get dpb address
        call bdos
        ld bc,13
        add hl,bc         ; now points to offset
        ld c,(hl)         ; c contains offset
        inc hl
        inc hl            ; now points to psh
        ld b,(hl)         ; contains psh
        ld e,b
        ld d,0            ; de contains psh
        ld hl,nsec        ; address of sector table
        add hl,de
        ld a,(hl)         ; number of sectors
        push af           ; number of sectors in a
        ld hl,msec        ; address of sectors per track
                          ;   table
        add hl,de
        ld a,(hl)         ; sectors per track
        ld (msecx),a      ; keep this
        ld hl,cread       ; address of table of primary
                          ;   load routines
        add hl,de
        add hl,de         ; points to address of plr
        ld e,(hl)
        inc hl
        ld d,(hl)         ; de contains address of plr
        ld (creada),de    ; store in creada
        ld hl,128         ; calculate sector length
        ld a,b
        and a
        jr z,zer          ; djnz does not handle b=0
dbbl:   add hl,hl         ; double hl
        djnz dbbl
zer:    ld (seclen),hl    ; sector length

; set track
        ld a,c            ; get directory offset
        srl a             ; dd => offset=4 => a=2 => track=1
                          ; sd => offset=2 => a=1 => track=1
        cp 3              ; make sure track <= 2
        jp nc,it          ; jump if invalid track
        ld l,a            ; track number to hl
        ld h,0
        ld (biosbc),hl    ; track number
        ld a,10
        ld (biospb),a     ; function code
        ld c,50           ; bios code call
        ld de,biospb      ; register image array
        call bdos

; set bank=1 for disk operations
        ld a,1
        ld (biosa),a      ; bank number
        ld a,28
        ld (biospb),a     ; set function = setbnk
        ld c,50           ; bios code call
        ld de,biospb      ; register image array
        call bdos

; load cpmldr to track 1 of disk
        pop bc            ; number of sectors of b
        ld hl,cpmldr      ; address of image of cpmldr
        ld (craddr),hl    ; keep address
        ld c,1            ; initial sector number
wloop:  push bc           ; sector count and number
        ld b,0
        ld (biosbc),bc    ; sector number

        ld a,11           ; bios set sector code
        ld (biospb),a     ; bios command
        ld c,50           ; bios call code
        ld de,biospb      ; register image array
        call bdos         ; set sector

                          ; set dma for disk ops
        ld bc,(craddr)    ; address of current seg
        ld (biosbc),bc
        ld hl,(seclen)    ; sector length
        add hl,bc         ; form address of next
        ld (craddr),hl    ;   segment of cpmldr
        ld a,12           ; bios set dma code
        ld (biospb),a     ; bios command
        ld c,50           ; bios call mode
        ld de,biospb      ; register image array
        call bdos
 
        ld a,14           ; bios write code
        ld (biospb),a     ; bios command
        ld c,50
        ld de,biospb      ; register image array
        call bdos         ; write
        and a
        jp nz,werr        ; jump if write error

        pop bc
        inc c             ; update sector number
        ld a,(msecx)      ; find number of sectors per track
        cp c
        jr nz,cy1
        ld c,1            ; first cycle complete
cy1:    inc c
        djnz wloop        ; read next segment

; set track = 0
        ld hl,0
        ld (biosbc),hl    ; track number
        ld a,10
        ld (biospb),a     ; function code
        ld c,50           ; bios code call
        ld de,biospb      ; register image array
        call bdos

; load primary loader to track 0 of disk
        ld bc,1           ; sector number
        ld (biosbc),bc    ; sector number
        ld a,11           ; bios set sector code
        ld (biospb),a     ; bios command
        ld c,50           ; bios call code
        ld de,biospb      ; register image array
        call bdos         ; set sector

                          ; set dma for disk ops
        ld bc,(creada)    ; address of primary ldr
        ld (biosbc),bc
        ld a,12           ; bios set dma code
        ld (biospb),a     ; bios command
        ld c,50           ; bios call mode
        ld de,biospb      ; register image array
        call bdos

        ld a,14           ; bios write code
        ld (biospb),a     ; bios command
        ld c,50
        ld de,biospb      ; register image array
        call bdos         ; write
        and a
        jp nz,werr        ; jump if write error

        jp ltd            ; function complete

oldver: ld de,ovm         ; not cpm3 error exit
        ld c,9            ; output string
        call bdos
        jp boot           ; to exit
ovm:    db 'Need cpm-3',cr,lf,'$'

operr:  ld de,oem         ; can't open cpmldr error exit
        ld c,9            ; output string code
        call bdos
        jp boot           ; to exit
oem:    db 'Cannot open cpmldr.com',cr,lf,'$'
rderr:  ld de,rem         ; read error
        ld c,9
        call bdos
        jp boot
rem:    db 'Read fails',cr,lf,'$'

; clear console input
clear:  ld c,11           ; to check console status
        call bdos
        and a             ; to set flags
        ret z             ; return if no character
        ld c,1            ; console input
        call bdos
        jr clear          ; cycle until console clear

lddm:   db 'Enter drive name; <ret> to quit: $'

exit:   ld c,13           ; reset disk system
        call bdos
        jp boot

slerr:  ld de,sem         ; select error
        ld c,9
        call bdos
        jp boot

sem:    db 'Disk select fails',cr,lf,'$'

it:     ld de,itm         ; invalid track
        ld c,9
        call bdos
        jp boot

itm:    db 'Invalid track',cr,lf,'$'

werr:   ld de,wem         ; write fails
        ld c,9
        call bdos
        jp boot

wem:    db 'Write error',cr,lf,'$'

rbuff:  db 1              ; buffer length
rbuffc: ds 2              ; number chars entered

; fcb for cpmldr
fcb:    db 1,'CPMLDR  COM',0,0,0,0
        ds 16
        db 0,0,0

; temporary storage for cpmldr
cpmldr: ds 1000h          ; 4k

craddr: ds 2              ; address of next portion
                          ;   of cpmldr to be loaded
seclen: ds 2              ; sector length
creada: ds 2              ; address of primary loader
msecx:  ds 1              ; sectors per track

; parameter block for making calls to bios
biospb: ds 1
biosa:  ds 1
biosbc: ds 2
biosde: ds 2
bioshl: ds 2

; number of sectors
nsec:   db 26             ; 26 128 byte blocks
        db 13             ; 13 256 byte blocks
        db 7              ; 7 512 byte blocks
        db 4              ; 4 1024 byte blocks

;number of sectors per track
msec:   db 26
        db 26
        db 15
        db 8

; code to read track 1 to core

rdcpm   macro ?nsec,?blkl,?msec
                          ; ?nsec = number of sectors
                          ; ?blkl = sector length
                          ; ?msec = sectors per track
        local rloop,rloop1,rok,sl1
        .phase 0e700h     ; to executed at 0e700h
        ld c,1
        call 0e00ch       ; track 1
        ld c,0
        call 0e030h       ; side 0
        ld c,0
        call 0e01bh       ; drive A
        ld bc,100h        ; dma
        call 0e012h       ; set dma
        ld bc,?nsec*100h+1; ?nsec sectors & sector number
rloop:  call 0e00fh       ; set sector
        push bc           ; store sector count & no.
        ld b,5            ; 5 tries to read
rloop1: push bc
        call 0e015h       ; do read
        pop bc
        jr nc,rok         ; jump out if ok
        djnz rloop1
rok:    jp c,0e02ah       ; error exit
        call 0e024h       ; get dma to bc
        ld hl,?blkl       ; length of block
        add hl,bc         ; increment dma
        ld b,h
        ld c,l
        call 0e012h       ; and set dma
        pop bc
        ld a,c
        cp ?msec-1        ; check for one cycle
        jr nz,sl1
        ld c,0            ; one cycle of disk complete
sl1:    inc c             ; increment sector no. by 2
        inc c
        djnz rloop        ; loop controlled by reg b
        jp 100h           ; jump to cmpldr
        .dephase
        endm

cread0: rdcpm 26,128,26
cread1: rdcpm 13,256,26
cread2: rdcpm 7,512,15
cread3: rdcpm 4,1024,8

cread:  dw cread0
        dw cread1
        dw cread2
        dw cread3

        end loadsy
